package com.lx.util;
//说明: json 解析


import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatterBuilder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.lx.util.LXJson.JSONNode.JSONType.*;

/**{ ylx } 2021/9/21 15:58 */
public class LXJson {

    public static void main(String[]args){
//        List ls = new ArrayList();
//        ls.add((new Date()));
//        ls.add(new DateTimeFormatterBuilder());
//        ls.add(new HashMap<>());
//        ls.add(new HashSet<>());
//        ls.add(1);
//        ls.add(1.2);
//        ls.add(new BigDecimal("0"));
//        ls.add(1123123123123L);
//        ls.add("1231");
//        String s = toJson(ls);
//        System.out.println(s);
//        List list = toList(s);
//        System.out.println(((Map)list.get(1)).get("padNextChar"));
    }

    public static Map<String, Object> toMap(String json){
        return toMap(JSONNode.toJsonNode(json));
    }
    public static List toList(String json){
        return toList(JSONNode.toJsonNode(json));
    }
    private static Map<String,Object> toMap(JSONNode.JSONNodeList jsonNodes){
        LX.exMsg(jsonNodes.poll().getJsonType() != LBS, "不是{开始!");
        JSONNode key = jsonNodes.poll();
        JSONNode val;
        Map<String, Object> map = new HashMap<>();
        while (key.getJsonType() == ISSTRING){
            JSONNode.JSONType colJsonType = jsonNodes.poll().getJsonType();
            LX.exMsg(!(colJsonType == COL || colJsonType == COL1), "不是:或=");
            val = jsonNodes.current();
            switch (val.getJsonType()){
                case LBT:
                    map.put(key.getTxt(), toList(jsonNodes));
                break;
                case LBS:
                    map.put(key.getTxt(), toMap(jsonNodes));
                    break;
                case ISSTRING:
                    map.put(key.getTxt(), jsonNodes.poll().getTxt());
                    break;
                case ISNULL:
                    jsonNodes.poll();
                    map.put(key.getTxt(), null);
                    break;
                case ISDECIMAL:
                    map.put(key.getTxt(), new BigDecimal(jsonNodes.poll().getTxt()));
                    break;
                case ISLONG:
                    map.put(key.getTxt(), Long.parseLong(jsonNodes.poll().getTxt()));
                    break;
                case ISTRUE :
                case ISFALSE:
                    map.put(key.getTxt(), Boolean.valueOf(jsonNodes.poll().getTxt()));
                    break;
                default:
                    LX.exMsg("解析异常:"+jsonNodes);
            }
            if (COM.isType(jsonNodes.current())){
                jsonNodes.poll();
                key = jsonNodes.poll();
            }else if (RBS.isType(jsonNodes.poll())){
                return map;
            }else{
                return LX.exMsg("解析异常:"+jsonNodes);
            }
        }
        return map;
    }
    private static List toList(JSONNode.JSONNodeList jsonNodes){
        LX.exMsg(jsonNodes.poll().getJsonType() != LBT, "不是[开始!");
        JSONNode val;
        List ls = new ArrayList<>();
        do{
            switch (jsonNodes.current().getJsonType()){
                case LBT:
                    ls.add(toList(jsonNodes));
                    break;
                case LBS:
                    ls.add(toMap(jsonNodes));
                    break;
                case ISSTRING:
                    ls.add(jsonNodes.poll().getTxt());
                    break;
                case ISNULL:
                    jsonNodes.poll();
                    ls.add(null);
                    break;
                case ISDECIMAL:
                    ls.add(new BigDecimal(jsonNodes.poll().getTxt()));
                    break;
                case ISLONG:
                    ls.add(Long.parseLong(jsonNodes.poll().getTxt()));
                    break;
                case ISTRUE :
                case ISFALSE:
                    ls.add(Boolean.valueOf(jsonNodes.poll().getTxt()));
                    break;
                case RBT:
                    jsonNodes.poll();
                    return ls;
                default:
                    LX.exMsg("解析异常:"+jsonNodes);
            }
            if (RBT.isType(jsonNodes.current())){
                jsonNodes.poll();
                return ls;
            }else if (COM.isType(jsonNodes.current())){
                jsonNodes.poll();
            }else{
                return LX.exMsg("解析异常:"+jsonNodes);
            }
        }while (true);
    }

    // Collection(Map List Set) Object[] Number String Date Enum Object
    public static String toJson(Object obj) {
        StringBuilder sb = new StringBuilder();
        try {
            toJson(obj, sb);
        } catch (Exception e) {
            LX.exMsg("解析异常:"+e);
        }
        return sb.toString();
    }
    private static void toJson(Object obj, StringBuilder sb) throws Exception {
        if (obj == null){
            sb.append("");
        }else if (obj instanceof String || obj instanceof Character){
            appendStr(sb, obj);
        }else if (obj instanceof Number || obj instanceof Boolean){
            sb.append(obj);
        }else if (obj instanceof Date){
            appendStr(sb,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(obj));
        }else if (obj instanceof Enum){
            appendStr(sb, ((Enum) obj).name());
        }else if(obj instanceof Map){
            sb.append("{");
            int i = 0;
            for (Object o : ((Map) obj).keySet()) {
                if (((Map) obj).get(o) != null){
                    if (i++!=0){
                        sb.append(",");
                    }
                    appendStr(sb, o);
                    sb.append(":");
                    StringBuilder s1 = new StringBuilder();
                    toJson(((Map) obj).get(o), s1);
                    sb.append(s1);
                }
            }
            sb.append("}");
        }else if (obj instanceof Collection){
            sb.append("[");
            int i = 0;
            for (Object e : (Collection<Object>)obj) {
                StringBuilder s1 = new StringBuilder();
                toJson(e,s1);
                if (i != 0){
                    sb.append(",");
                }
                i++;
                sb.append(s1);
            }
            sb.append("]");
        }else if (obj.getClass().isArray()){
            sb.append("[");
            for (int i = 0, length = Array.getLength(obj); i < length; i++) {
                StringBuilder s1 = new StringBuilder();
                toJson(Array.get(obj, i),s1);
                sb.append(",");
                sb.append(s1);
            }
            sb.append("]");
        }else{
            Map<String, Object> fields = toField(obj);
            toJson(fields, sb);
        }
    }
    //说明: 获取Bean中的所有字段
    /**{ ylx } 2021/9/21 17:28 */
    private static Map<String, Object> toField(Object obj) throws IllegalAccessException {
        Map<String,Object> fs = new HashMap<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (excludeField(field)) {
                continue;
            }
            field.setAccessible(true);
            if (field.get(obj) != null && field.get(obj) != obj){
                fs.put(field.getName(),field.get(obj));
            }
        }
        return fs;
    }
    //说明: 字段是否需要排除
    /**{ ylx } 2021/9/21 20:44 */
    static boolean excludeField(Field f) {
        return (((Modifier.TRANSIENT | Modifier.STATIC) & f.getModifiers()) != 0)  || f.isSynthetic()
                || (!Enum.class.isAssignableFrom(f.getType()) && (f.getType().isAnonymousClass() || f.getType().isLocalClass()));
    }

    //说明:拼接字符串
    /**{ ylx } 2021/9/21 17:08 */
    private static void appendStr(StringBuilder sb, Object o) {
        sb.append("\"");
        escape(o.toString(), sb);
        sb.append("\"");
    }
    //说明: 转换特殊字符串
    /**{ ylx } 2021/9/21 20:45 */
    private static void escape(String s, StringBuilder sb) {
        for(int i=0; i<s.length(); i++){
            char ch = s.charAt(i);
            switch(ch){
                case '"':
                    sb.append("\\\"");
                    break;
                case '\\':
                    sb.append("\\\\");
                    break;
                case '\b':
                    sb.append("\\b");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '/':
                    sb.append("\\/");
                    break;
                default:
                    if((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) {
                        String str = Integer.toHexString(ch);
                        sb.append("\\u");
                        for(int k=0; k<4-str.length(); k++) {
                            sb.append('0');
                        }
                        sb.append(str.toUpperCase());
                    }else{
                        sb.append(ch);
                    }
            }
        }
    }

    public static <T> T toObj(Class<T> t, String obj) {
        if (t == null || obj == null){
            return null;
        }
        if (t.isAssignableFrom(obj.getClass())){
            return (T) obj;
        }
        switch (t.getSimpleName()){
            case "boolean":
            case "Boolean":
                return (T)Boolean.valueOf((String)obj);
            case "char":
            case "Character":
                LX.exMsg(obj.length() != 1 ,"无法将["+obj+"]转换为char");
                Character c = obj.charAt(0);
                return (T)c;
            case "int":
            case "Integer":
                return (T)Integer.valueOf((String)obj);
            case "long":
            case "Long":
                return (T)Long.valueOf((String)obj);
            case "double":
            case "Double":
                return (T)Double.valueOf((String)obj);
            case "float":
            case "Float":
                return (T)Float.valueOf((String)obj);
            case "byte":
            case "Byte":
                return (T)Byte.valueOf((String)obj);
            case "short":
            case "Short":
                return (T)Short.valueOf((String)obj);
        }
        LX.exMsg(!t.isAssignableFrom(Map.class),"可转换的类型为基础类型和Map");
        return (T)LX.toMap(obj);
    }


    /**{ ylx } 2021/9/19 21:20 */
    public static class JSONNode {
        /**
         * 文字类型
         */
        private JSONType jsonType;
        private String txt;

        public JSONType getJsonType() {
            return jsonType;
        }

        public String getTxt() {
            return txt;
        }

        public JSONNode(JSONType jsonType, String txt) {
            this.jsonType = jsonType;
            this.txt = txt;
        }

        public static final String SYMBOL_REGULAR="\\{|\\}|\\[|\\]|,|:|=";

        public static class JSONNodeList{
            private LinkedList<JSONNode> nodes;

            public JSONNodeList(LinkedList<JSONNode> nodes) {
                this.nodes = nodes;
            }
            public boolean hasNext(){
                return nodes.size()>0;
            }
            //说明: 弹出当前节点
            /**{ ylx } 2021/9/20 20:00 */
            public JSONNode poll(){
                LX.exMsg(!hasNext(),"下个节点不存在!");
//                LXLog.debug("取出当前节点:"+current());
                return nodes.poll();
            }
            //说明: 获取当前节点
            /**{ ylx } 2021/9/20 20:00 */
            public JSONNode current(){
                LX.exMsg(!hasNext(),"下个节点不存在!");
                return nodes.get(0);
            }

            @Override
            public String toString() {
                return "{" +
                        "nodes=" + nodes +
                        '}';
            }
        }
        //说明: 解析成JSON字符
        /**{ ylx } 2021/9/19 19:38 */
        public static JSONNodeList toJsonNode(String data) {
            LinkedList<JSONNode> jsonNodes = new LinkedList<>();
            StringBuilder sb = new StringBuilder(data);
            //单引号开启
            boolean isSingleQuotes = false;
            int len1,len2,len3 = -1;
            while ((len1=sb.indexOf("\"")) != -1 | (len3 = sb.indexOf("'")) != -1){
                //截取到 开始' 或 "
                if ((isSingleQuotes=(len3 != -1 && (len1 == -1 || len3<len1)))){
                    len1 = len3;
                }
                //检查'前面的字符串是否异常
                String start = sb.substring(0,len1);
                parse(start,SYMBOL_REGULAR,jsonNodes);
                //获取后续的 非 \"
                int s1 = len1+1;
                do{
                    len2 = sb.indexOf(isSingleQuotes?"'":"\"", s1);
                    if (sb.length()==len2 || '\\' != sb.charAt(len2-1)){
                        //当前 引号前面不是 \
                        break;
                    }
                    s1 = len2+1;
                }while(true);
                if (len2 == -1){
                    LX.exMsg("解析异常:"+data);
                }
                String str = sb.substring(len1+1,len2).replace("\'","\"").replace("\\\"","\"");
                jsonNodes.add(new JSONNode(JSONType.ISSTRING, str));
                sb.delete(0,len2+1);

            }
            if (sb.length()>0){
                parse(sb.toString(),SYMBOL_REGULAR,jsonNodes);
            }
            return new JSONNodeList(jsonNodes);
        }
        //说明: 解析字符串为字段
        /**{ ylx } 2021/9/19 20:59 */
        public static void parse(String str, String symbolRegular , List<JSONNode> jsonNodes) {
            Arrays.asList(str.split("\\s+")).forEach(s->{
                //按照空白字符拆分
                Pattern p = Pattern.compile(symbolRegular);
                Matcher m = p.matcher(s);
                int start,end = 0;
                do {
                    if (m.find()) {
                        start = m.start();
                        if (start>end){
                            String s1= s.substring(end,start);
                            jsonNodes.add(new JSONNode(JSONType.getType(s1),s1));
                        }
                        String s1 = m.group();
                        jsonNodes.add(new JSONNode(JSONType.getType(s1),s1));
                        end = m.end();
                    } else {
                        if (s.length()>end){
                            String s1= s.substring(end);
                            jsonNodes.add(new JSONNode(JSONType.getType(s1),s1));
                        }
                        break;
                    }
                }while (true);
            });
        }


        public enum JSONType{
            /** 界限符号*/
            LBT("["),RBT("]"),LBS("{"),RBS("}"),COM(","),COL(":"),COL1("=")
            /** 是数字 */
            ,ISSTRING("string"),ISLONG("isLong"),ISDECIMAL("isDecimal"),ISTRUE("true"),ISFALSE("false"),ISNULL("null")
            ;
            private String type;
            JSONType(String type){
                this.type = type;
            }
            //说明: 类型相同
            /**{ ylx } 2021/9/20 11:41 */
            public boolean isType(JSONNode node){
                return node != null && this == node.getJsonType();
            }

            private static final Map<String, JSONType> TYPE_MAP = new HashMap<>();
            static {
                for (JSONType type : values()) {
                    TYPE_MAP.put(type.type, type);
                }
            }
            // 获取当前字符串的类型
            public static JSONType getType(String str){
                str = str.toLowerCase();
                if (TYPE_MAP.containsKey(str)){
                    return TYPE_MAP.get(str);
                }
                if (str.matches("-?\\d+")){
                    return ISLONG;
                }else if (str.matches("-?\\d+.\\d+")){
                    return ISDECIMAL;
                }else if ("true".equals(str)){
                    return ISTRUE;
                }else if ("false".equals(str)){
                    return ISFALSE;
                }else if ("null".equals(str)){
                    return ISNULL;
                }
                LX.exMsg("异常字符串:"+str);
                return null;
            }
        }

        @Override
        public String toString() {
            return "{ "+jsonType +":\""+ txt +"\" }";
        }
    }


}
